package com.cx.framework;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author:cxy
 * @Date:2020/10/11 16:01
 */
public class CxApplicationContext {

    //配置扫描类
    private Class<?> configClass;

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();

    private Map<String,Object> singletonObjects = new HashMap<>();

    public CxApplicationContext(Class<?> configClass) {
        this.configClass = configClass;
        //扫描
        scan(configClass);
        //创建非懒加载的单例bean
        createNonLazyBean();

    }


    private Object creatBean(BeanDefinition beanDefinition,String beanName) {

        //1.创建对象
        try {
            Class<?> beanClass = beanDefinition.getBeanClass();
            Object instance = beanClass.getDeclaredConstructor().newInstance();

            //填充属性
            for (Field field : beanClass.getDeclaredFields()) {

                if(!field.isAnnotationPresent(Autowrited.class)){
                    continue;
                }
                //属性赋值  byType  byName
                Object bean = getBean(field.getName());
                field.setAccessible(true);
                field.set(instance,bean);
            }

            if(instance instanceof BeanNameAware){
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            if(instance instanceof InitializingBean){
                ((InitializingBean)instance).afterPropertiesSet();
            }

            // aop...

            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }


        return null;
    }


    public Object getBean(String beanName){
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NoClassDefFoundError();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if("singleton".equals(beanDefinition.getScope())){
            // 单例池 获取...
            Object o = singletonObjects.get(beanName);
            if(o == null){
                o = creatBean(beanDefinition,beanName);
                singletonObjects.put(beanName,o);
            }
            return o;
        }

        if("prototype".equals(beanDefinition.getScope())){
            //原型bean 每次都创建
            return creatBean(beanDefinition,beanName);
        }

        return null;
    }

    private void createNonLazyBean() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton")&& !beanDefinition.isLazy()){
                //创建bean
                Object bean = creatBean(beanDefinition,beanName);
                singletonObjects.put(beanName,bean);
            }
        }
    }

    private void scan(Class<?> configClass) {
        ClassLoader classLoader = CxApplicationContext.class.getClassLoader();
        //1.判断
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //2.扫描
            ComponentScan annotation = configClass.getAnnotation(ComponentScan.class);
            String[] path = annotation.path(); // com.cx.service
//            System.out.println(Arrays.toString(path));

            URL resource = classLoader.getResource(path[0].replace(".", "/"));

            assert resource != null;
            File file = new File(resource.getFile()); //文件夹
            //3.获取到class文件
            for (File f : Objects.requireNonNull(file.listFiles())) {
                String absolutePath = f.getAbsolutePath();
//                System.out.println(absolutePath); // E:\cxy\workSpace\Cx_project\CX_Spring\target\classes\UserService.class

                if(!absolutePath.endsWith(".class")){
                    continue;
                }
                try {
                    Class<?> loadClass = classLoader.loadClass(absolutePath.substring(absolutePath.indexOf(path[0].replace(".", "\\")), absolutePath.indexOf(".class")).replace("\\", "."));
//                    System.out.println(loadClass); //class com.cx.service.UserService

                    //判断是否bean
                    if(loadClass.isAnnotationPresent(Component.class)){
                        //定义beanDefinition
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setBeanClass(loadClass);

                        Component componentAnnotation = loadClass.getAnnotation(Component.class);
                        String beanName = componentAnnotation.value();
                        beanDefinitionMap.put(beanName,beanDefinition);

                        //判断是否懒加载
                        if(loadClass.isAnnotationPresent(Lazy.class)){
                            beanDefinition.setLazy(true);
                        }

                        //判断是否原型
                        if(loadClass.isAnnotationPresent(Scope.class)  ){
                            String value = loadClass.getAnnotation(Scope.class).value();
                            beanDefinition.setScope(value);

                        }else{
                            //单例
                            beanDefinition.setScope("singleton");
                        }

                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Class<?> getConfigClass() {
        return configClass;
    }

    public void setConfigClass(Class<?> configClass) {
        this.configClass = configClass;
    }
}
